#! /usr/bin/env python
# encoding: utf-8

"""
The `build_register` function loads the configuration from the file
given in input, loading it with the PyYAML module.

Then it calls various internal function, that handle the specific settings,
parsing the values and making the opportune checkings to verify the correctness
of the settings to avoid runtime exceptions.
"""

import sys
import re
import logging

import yaml

from spyro.internal.utils.io import safe_open
from spyro.config.register import Register

log = logging.getLogger(__name__)

def build_register(configname):
    with safe_open(configname) as (fd, error):
        if error:
            raise error
        
        # we store the file name for future usages
        setattr(Register, "_fname", configname)

        # else
        log.debug("Beginning the parsing of %r." % configname)

        config = yaml.load(fd.read())

        # if the file is blank we have no settings (and routes are needed)
        if config is None:
            raise TypeError("You've supplied a void configuration file.")

        settings = [('routes', _parse_routes, None),
                    ('pages', _parse_pages, _default_pages),
                    ('debug', _parse_debug, _default_debug),
                    ('templates', _parse_templates, _default_templates),
                    ('i18n', _parse_i18n, _default_i18n),
                    ('sessions', _parse_sessions, _default_sessions),
                    ('contrib', _parse_contrib, None),]

        for option, function, default in settings:
            if option in config:
                function(config[option])
            else:
                setattr(Register, option, default)

def _parse_routes(config):
    """It parses the `routes` configuration section.
    Checks with a regexp if the given controller description is
    """

    if config is None:
        raise ValueError("You must declare at least one route.")
    elif not isinstance(config, dict):
        raise TypeError("The 'routes' section is misconfigured.")

    log.debug("Analyzing the `routes` config section.")

    routes = dict()
    prefix = None
    match = re.compile('(.*)\.(.*):(.*)').match

    for key, value in config.items():
        if key == 'prefix' and isinstance(value, str):
            prefix = value
        elif isinstance(key, str) and isinstance(value, str):
            if bool(match(value)):
                # compiling the key as regexp
                key = re.compile(key)
                routes[key] = value
            else:
                raise ValueError("'routes': bad format for %r:%r." %
                                 (key, value))
        else:
            raise ValueError("'routes': wrong type for key or value (%r:%r)" %
                             (key, value))

    if prefix is not None:
        for key, value in routes.items():
            routes[key] = "%(prefix)s.%(value)s" % locals()

    setattr(Register, 'routes', routes)

def _parse_pages(config):
    """It parses the `routes` configuration section.
    """

    if not isinstance(config, dict):
        raise TypeError("The 'pages' section is misconfigured.")

    log.debug("Analyzing the `pages` config section.")

    pages = dict()

    for key, value in config.items():
        if int(key) == 404:
            pages[404] = value
        elif int(key) == 500:
            pages[500] = value
        else:
            raise ValueError("'pages': unknown key (%r)" % (key))

    # XXX: it doesn't check anymore if the page is loadable

    if not 404 in pages:
        pages[404] = _default_pages[404]
    if not 500 in pages:
        pages[500] = _default_pages[500]

    setattr(Register, 'pages', pages)

def _parse_debug(config):
    """Parses the `debug` flag.
    """
    if not isinstance(config, bool):
        raise TypeError("The 'debug' section is misconfigured.")

    log.debug("Setting the `debug` flag to: %r" % config)

    setattr(Register, 'debug', config)

def _parse_templates(config):
    """It parses the `templates` section.
    """

    if not isinstance(config, dict):
        raise TypeError("The 'templates' section is misconfigured.")

    log.debug("Analyzing the `templates` config section.")

    templates = dict()

    for key, value in config.items():
        if key == 'root' and isinstance(value, str):
            templates['root'] = value
        else:
            raise ValueError("'templates': unknown key (%r)" % (key))

    if not 'root' in templates:
        templates['root'] = _default_templates['root']

    setattr(Register, 'templates', templates)

def _parse_i18n(config):
    """It parses the `i18n` specific section.
    """

    if not isinstance(config, dict):
        raise TypeError("The 'templates' section is misconfigured.")

    log.debug("Analyzing the `i18n` config section.")

    i18n = dict()

    for key, value in config.items():
        if key == 'domain' and isinstance(value, str):
            i18n[key] = value
        elif key == 'fallback' and isinstance(value, bool):
            i18n[key] = value
        elif key == 'localedir' and isinstance(value, str):
            i18n[key] = value
        elif key == 'installed_langs' and isinstance(value, str):
            i18n[key] = [lang.strip() for lang in value.split(',')]
        elif key == 'default' and isinstance(value, str):
            i18n[key] = value
        else:
            raise ValueError("'i18n': unknown key (%r) or wrong value" % (key))

    if not 'domain' in i18n:
        i18n['domain'] = _default_i18n['domain']
    if not 'fallback' in i18n:
        i18n['fallback'] = _default_i18n['fallback']
    if not 'localedir' in i18n:
        i18n['localedir'] = _default_i18n['localedir']
    if not 'installed_langs' in i18n:
        i18n['installed_langs'] = _default_i18n['installed_langs']
    if not 'default' in i18n:
        i18n['default'] = _default_i18n['default']

    setattr(Register, 'i18n', i18n)

def _parse_sessions(config):
    """It parses the `sessions` configuration section.
    """

    if not isinstance(config, dict):
        raise TypeError("The 'sessions' section is misconfigured.")

    log.debug("Analyzing the `sessions` config section.")

    sessions = dict()

    for key, value in config.items():
        if key == 'cookie' and isinstance(value, str):
            sessions[key] = value
        elif key == 'storage' and isinstance(value, str):
            sessions[key] = value
        elif key == 'secret' and isinstance(value, str):
            sessions[key] = value
        elif key == 'expiration' and isinstance(value, str):
            sessions[key] = value
        elif key == 'folder' and isinstance(value, str):
            sessions[key] = value
        else:
            raise ValueError("'sessions': unknown key (%r) or wrong value" %
                             (key))

    if not 'cookie' in sessions:
        sessions['cookie'] = _default_sessions['cookie']
    if not 'storage' in sessions:
        sessions['storage'] = _default_sessions['storage']
    if not 'secret' in sessions:
        sessions['secret'] = _default_sessions['secret']
    if not 'expiration' in sessions:
        sessions['expiration'] = _default_sessions['expiration']
    if not 'folder' in sessions:
        sessions['folder'] = _default_sessions['folder']

    setattr(Register, 'sessions', sessions)

def _parse_contrib(config):
    """It saves the contrib section as-is, with no value testing.
    """

    setattr(Register, 'contrib', config)

_default_pages = {
    404: 'spyro.internal.errorpage.Pages:not_found',
    500: 'spyro.internal.errorpage.Pages:internal_error'
}

_default_debug = False

_default_templates = {
    'root': 'views'
}

_default_i18n = {
    'domain': str(),
    'fallback': False,
    'localedir': str(),
    'installed_langs': str(),
    'default': str()
}

_default_sessions = {
    'cookie': 'sessid',
    'storage': 'hard-disk',
    'secret': 'spyro-secret-keyword',
    'expiration': '29500',
    'folder': '/tmp/spyro-sessions'
}
